/*
 * Investigate.h
 *
 *  Created on: Jan 29, 2010
 *      Author: anton.kuhlevskyi
 */

#pragma once

#include <stdexcept>
#include <sstream>

// Possible node kinds
enum NodeKind
{
	NK_NULL,
	NK_BINARY,
	NK_MULTIPLE
};

enum NodeLifeCycleState
{
	NLCS_STATIC,
	NLCS_DINAMIC
};

// Possible node types
enum NodeType
{
    NT_OR,
    NT_AND,
    NT_NOT,
    NT_ATOM
};

template<NodeKind _node_kind=NK_NULL>
class _BaseNode
{
private:
	_BaseNode(const _BaseNode&);
	_BaseNode& operator=(const _BaseNode&);

protected:
	_BaseNode(){};
	~_BaseNode(){};
};

template<NodeLifeCycleState _node_lc_sate=NLCS_STATIC>
class _BaseBinaryNode
	: public _BaseNode<NK_BINARY>
{
	const static size_t m_maxSize=2;

	const size_t m_size;
	_BaseBinaryNode* m_children[m_maxSize];


protected:
	_BaseBinaryNode(const size_t size,
					_BaseBinaryNode* lh=NULL,
					_BaseBinaryNode* rh=NULL)
		: m_size(size)
	{
		m_children[0] = lh;
		m_children[1] = rh;
	};

public:
	virtual ~_BaseBinaryNode()
	{
    	delete m_children[0];
    	delete m_children[1];
	};
	inline size_t getChildrenCount()
	{
		return m_size;
	};
	inline _BaseBinaryNode* getChild(size_t idx)
	{
		if (idx >= getChildrenCount())
			throw std::runtime_error("::getChild() - incorrect index");

		return m_children[idx];
	};
	inline static NodeKind getNodeKind()
	{
		return NK_BINARY;
	}
};

template<>
class _BaseBinaryNode<NLCS_DINAMIC>
	: public _BaseNode<NK_BINARY>
{
	const static size_t m_maxSize=2;

	const size_t m_size;
	_BaseBinaryNode* m_children[m_maxSize];

protected:
	_BaseBinaryNode(const size_t size,
					_BaseBinaryNode* lh=NULL,
					_BaseBinaryNode* rh=NULL)
			: m_size(size)
	{
		m_children[0] = lh;
		m_children[1] = rh;
	};

public:
	virtual ~_BaseBinaryNode()
	{
    	delete m_children[0];
    	delete m_children[1];
	};
	inline size_t getChildrenCount()
	{
		return m_size;
	};
	inline _BaseBinaryNode* getChild(size_t idx)
	{
		if (idx >= getChildrenCount())
			throw std::runtime_error("::getChild() - incorrect index");

		return m_children[idx];
	};
	inline _BaseBinaryNode* setChild(size_t idx, _BaseBinaryNode* pNode)
	{
        if(idx >= getChildrenCount())
        	throw std::runtime_error("setChild() - incorrect index");

        _BaseBinaryNode* pOldNode = m_children[idx];
        m_children[idx]=pNode;
        return pOldNode;
	};
	inline static NodeKind getNodeKind()
	{
		return NK_BINARY;
	};
};

typedef _BaseBinaryNode<> Static;
typedef _BaseBinaryNode<NLCS_DINAMIC> Dynamic;

// NODE AND, OR
template < NodeType _node_type
		 , class _base_binary = Static >
class BinaryNodeImpl
	: public _base_binary
{
	const static NodeType m_type=_node_type;

public:
	typedef _base_binary BinaryNode;

	BinaryNodeImpl(BinaryNode* lh=NULL,
				   BinaryNode* rh=NULL)
		: _base_binary(2, lh, rh)
	{};
	virtual ~BinaryNodeImpl(){};

	inline NodeType getNodeType()
	{
		return m_type;
	};
	BinaryNodeImpl* clone()
	{
		return new BinaryNodeImpl(this->getChild(0),this->getChild(1));
	};
};

// NODE NOT
template <class _base_binary>
class BinaryNodeImpl<NT_NOT, _base_binary>
	: public _base_binary
{
	const static NodeType m_type=NT_NOT;

public:
	typedef _base_binary BinaryNode;

	BinaryNodeImpl(BinaryNode* lh=NULL)
		: _base_binary(1, lh)
	{};
	virtual ~BinaryNodeImpl(){};

	inline NodeType getNodeType()
	{
		return m_type;
	};
	BinaryNodeImpl* clone()
	{
		return new BinaryNodeImpl(this->getChild(0));
	};
};

// NODE ATOM
template <class _base_binary>
class BinaryNodeImpl<NT_ATOM, _base_binary>
	: public _base_binary
{
	const static NodeType m_type=NT_ATOM;
	std::string m_text;

public:
	typedef _base_binary BinaryNode;

	BinaryNodeImpl(const std::string & sText)
		: _base_binary(0)
		, m_text(sText)
	{};
	virtual ~BinaryNodeImpl(){};

	inline std::string getAtomText() const
	{
		return m_text;
	};
	inline NodeType getNodeType()
	{
		return m_type;
	};
	BinaryNodeImpl* clone()
	{
		return new BinaryNodeImpl(m_text);
	};
};
